package linkedlist.leetcode_148_medium;

import java.util.HashSet;

/**
 * 排序链表
 */
public class BestSolution {
    public static void main(String[] args) {
        BestSolution s = new BestSolution();
        ListNode head1 = new  ListNode(4);
        head1.next = new  ListNode(3);
        head1.next.next = new  ListNode(2);
        head1.next.next.next = new  ListNode(6);
        head1.next.next.next.next = new  ListNode(5);
        head1.next.next.next.next.next = new  ListNode(7);
        head1.next.next.next.next.next.next = new  ListNode(1);
        ListNode listNode = s.sortList(head1);
        s.printLinkedList(listNode);
    }
    public static void printLinkedList(ListNode node) {
        System.out.print("Linked List: ");
        while (node != null) {
            System.out.print(node.val + " ");
            node = node.next;
        }
        System.out.println();
    }
    public ListNode sortList(ListNode head) {
        //如果只有一个节点直接返回
        if (head == null || head.next == null) {
            return head;
        }
        //获取链表长度
        int len = ListNodeLength(head);

        //定义哨兵节点用于返回
        ListNode dummyNode = new ListNode(-1);
        dummyNode.next = head;

        //循环时间复杂度O(logn)，外层控制轮次，内层进行分割
        //i控制分割步长，指数级增长 1,2,4,8...步长超过链表长度就是排序完成
        for (int i = 1; i < len; i = i <<1) {
            //设置每一轮分割的起始节点
            ListNode pre = dummyNode;
            ListNode curNode = dummyNode.next;
            //每一轮都从curNode开始，也就是每一轮排序后的头节点，那么终止条件自然就是curNode != null
            while(curNode != null){
                ListNode leftNode = curNode;
                ListNode rightNode = cut(leftNode,i);
                //寻找分割的起始节点
                curNode = cut(rightNode,i);
                //通过pre节点指向排序后的头节点，作为下一轮分割的起始节点
                pre.next = mergeTwoLinkedList(leftNode,rightNode);
                while (pre.next != null){
                    pre = pre.next;
                }
            }
        }
        return dummyNode.next;
    }

    /**
     * 分割，断链。
     * @param head      进行断链操作的节点
     * @param step      分割步长，指数级增长，1-2-4-8...
     * @return          保存断链节点的下一个节点，作为下一组节点的开始节点
     */
    private ListNode cut(ListNode head, int step) {
        if (head == null){
            return null;
        }
        //设置分割步长step
        for (int i = 1; head.next != null && i < step; i++){
            head = head.next;
        }
        ListNode rightNode = head.next;
        //断链
        head.next = null;
        return rightNode;
    }

    public int ListNodeLength(ListNode head) {
        int length = 0;
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
            length++;
        }
        return length;
    }

    public ListNode mergeTwoLinkedList(ListNode l1, ListNode l2) {
        ListNode dummyNode = new ListNode(-1);
        ListNode cur = dummyNode;
        while (l1 != null && l2 != null) {
            if (l1.val < l2.val) {
                cur.next = l1;
                l1 = l1.next;
            } else {
                cur.next = l2;
                l2 = l2.next;
            }
            cur = cur.next;
        }
        cur.next = l1 == null ? l2 : l1;
        return dummyNode.next;
    }
}
